package org.scrm.util;


import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.util.CollectionUtils;

import javax.net.ssl.SSLContext;
import java.io.*;
import java.nio.charset.CodingErrorAction;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author xu yuanjun
 * @data 2019/1/30 13:57
 */
@Slf4j
public class HttpClientUtil {


    //连接池最大连接数
    private final static Integer MAX_TOTAL=500;

    //路由最大连接数
    private final static Integer MAX_PER_ROUTE=500;

    //连接建立后数据传输超时时长
    private final static Integer socketTimeout = 3000000;

    // 建立连接超时时长
    private final static Integer connectTimeout = 3000000;

    //建立请求超时时长
    private final static Integer connectionRequestTimeout = 3000000;

    private final static String DEFAULT_CHARSET="UTF-8";

    //连接管理器
    private static PoolingHttpClientConnectionManager connManager = null;

    public static final String success_key="success";
    public static final String success_value="true";
    public static final String returnStr="returnStr";
    public static final String returnStr_value="NULL";

    //静态初始化
    static{
        //创建http,https构建器
        RegistryBuilder<ConnectionSocketFactory> registryBuilder=RegistryBuilder.<ConnectionSocketFactory>create();
        //注册hhtp协议
        registryBuilder.register("http", new PlainConnectionSocketFactory());

        try {
            //创建SSL连接工厂，并将自定义的SSLContext和允许任何主机名通过校验的指令传入
            LayeredConnectionSocketFactory sslSf=new SSLConnectionSocketFactory(SSLContext.getDefault());
            //将SSL自定义工厂注册到hhtps协义上
            registryBuilder.register("https", sslSf);
        } catch (NoSuchAlgorithmException e) {
            log.error(e.getMessage());
        }

        //创建注册器
        Registry<ConnectionSocketFactory> registry=registryBuilder.build();

        //创建连接管理器
        connManager=new PoolingHttpClientConnectionManager(registry);
        //创建连接管理器配置
        ConnectionConfig connectionConfig=ConnectionConfig.custom()
                .setMalformedInputAction(CodingErrorAction.IGNORE)
                .setUnmappableInputAction(CodingErrorAction.IGNORE)
                .setCharset(Consts.UTF_8)
                .build();
        connManager.setDefaultConnectionConfig(connectionConfig);
        //设置连接池最大连接数
        connManager.setMaxTotal(MAX_TOTAL);
        //设置单个路由最大的连接线程数量
        connManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);
    }

    /**
     * 获取HttpClient
     * @return
     */
    private static CloseableHttpClient getCloseableHttpClient(){
        //上下文
        HttpClientContext httpClientContext=HttpClientContext.create();
        //会话
        CookieStore cookieStore=new BasicCookieStore();
        httpClientContext.setCookieStore(cookieStore);

        //请求配置
        RequestConfig globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.BROWSER_COMPATIBILITY).build();

        return HttpClients.custom().setConnectionManager(connManager)
                .setDefaultRequestConfig(globalConfig)
                .setDefaultCookieStore(cookieStore).build();
    }

    /**
     * URL带参数，需要编码后传入
     * @param url
     * @param charset
     * @return
     */
    public static String get(String url,String charset){
        return get(url,null,charset);
    }

    /**
     * GET请求，包含请求参数
     * @param url
     * @param paramsMap
     * @param charset
     * @return
     */
    public static String get(String url,Map<String,String> paramsMap,String charset){
        if(StringUtils.isEmpty(url)){
            return null;
        }
        List<NameValuePair> paramslist=getParamsList(paramsMap);
        if(!CollectionUtils.isEmpty(paramslist)){
            url=url+"?"+URLEncodedUtils.format(paramslist, getCharset(charset));
        }
        String excute = excute(new HttpGet(url), charset);
        log.info("url:"+url+" param:"+paramsMap+" res"+excute);
        return excute;
    }


    /**
     * http 请求，包含请求参数和字符集
     * @param url
     * @param paramsMap
     * @param charset
     * @return
     */
    public static String sendRequest(String url,Map<String,String> paramsMap,String charset,String mothod){
        if("get".equalsIgnoreCase(mothod)){
            return get(url, paramsMap, charset);
        }else if("post".equalsIgnoreCase(mothod)){
            return post(url, paramsMap, charset);
        }
        return null;
    }
    public static String sendRequest(String url,Map<String,String> paramsMap,String charset,String mothod,String sendFormat){
        if("json".equals(sendFormat)){
            return postJSON(url, JSON.toJSONString(paramsMap), charset);
        }else{
            return sendRequest(url, paramsMap, charset, mothod);
        }
    }

    /**
     * POST请求，包含请求参数和字符集
     * @param url
     * @param paramsMap
     * @param charset
     * @return
     */
    public static String post(String url,Map<String,String> paramsMap,String charset){
        if(StringUtils.isEmpty(url)){
            return null;
        }
        List<NameValuePair> paramslist=getParamsList(paramsMap);
        HttpPost httpPost=new HttpPost(url);
        try {
            if(!CollectionUtils.isEmpty(paramslist)){
                httpPost.setEntity(new UrlEncodedFormEntity(paramslist,getCharset(charset)));
            }
        } catch (UnsupportedEncodingException e) {
            log.error("UrlEncodedFormEntity转换字符异常",e);
            return null;
        }
        String excute= excute(httpPost,charset);
        log.info("url:"+url+" param:"+paramsMap+" res"+excute);
        return excute;
    }

    /**
     * POST请求，包含请求参数和字符集
     * @param url
     * @param paramList 支持更丰富的参数表达，如数组
     * @param charset
     * @return
     */
    public static String post(String url,List<NameValuePair> paramList,String charset){
        if(StringUtils.isEmpty(url)){
            return null;
        }
        HttpPost httpPost=new HttpPost(url);
        try {
            if(!CollectionUtils.isEmpty(paramList)){
                httpPost.setEntity(new UrlEncodedFormEntity(paramList,getCharset(charset)));
            }
        } catch (UnsupportedEncodingException e) {
            log.error("UrlEncodedFormEntity转换字符异常",e);
            return null;
        }
        String excute= excute(httpPost,charset);
        log.info("url:"+url+" param:"+paramList+" res"+excute);
        return excute;
    }

    /**
     * 只接收String类型的参数请求，并是POST的请求
     * @param url
     * @param params
     * @param charset
     * @return
     */
    public static String post(String url,String params,String charset){
        if (StringUtils.isEmpty(url)) {
            log.error("url不能为空");
            return null;
        }
        HttpPost httpPost=new HttpPost(url);
        httpPost.setEntity(new StringEntity(params,getCharset(charset)));

        return excute(httpPost,charset);
    }
    public static String postJSON(String url,String jsonString,String charset){
        if (StringUtils.isEmpty(url)) {
            log.error("url不能为空");
            return null;
        }
        HttpPost httpPost=new HttpPost(url);
        httpPost.setEntity(new StringEntity(jsonString,ContentType.APPLICATION_JSON));
        String res= excute(httpPost,charset);
        log.info("url:"+url+" json param:"+jsonString+" res:"+res);
        return res;
    }

    public static String postFile(String url,String jsonString,String charset){
        if (StringUtils.isEmpty(url)) {
            log.error("url不能为空");
            return null;
        }
        HttpPost httpPost=new HttpPost(url);
        httpPost.setEntity(new StringEntity(jsonString,ContentType.APPLICATION_JSON));
        return excute(httpPost,charset);
    }

    /**
     * 通用返回类型为String的执行
     * @param request
     * @param charset
     * @return
     */
    private static String excute(HttpRequestBase request,String charset){
        //请求对象
        CloseableHttpClient httpClient=getCloseableHttpClient();
        request.setConfig(getRequestConfig());
        //响 应对象
        CloseableHttpResponse httpResponse = null;
        //返回结果
        String responseStr=null;
        try {
            //调用远程接口
            httpResponse=httpClient.execute(request);
            //查看返回的状态码是否调用成功
            if(httpResponse.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
                log.error("接口调用失败，状态码："+httpResponse.getStatusLine().getStatusCode());
                return null;
            }
            //获取返回的结果数据(String类型)
            responseStr=EntityUtils.toString(httpResponse.getEntity(),getCharset(charset));
        } catch (ClientProtocolException e) {
            log.error("客户端连接异常：",e);
            return null;
        } catch (IOException e) {
            log.error("IO异常：",e);
            return null;
        }finally{
            close(httpResponse);
        }
        return responseStr;
    }

    /**
     * 关闭资源
     * @param httpResponse
     */
    private static void close(CloseableHttpResponse httpResponse){
        if(httpResponse!=null){
            try {
                httpResponse.close();
            } catch (IOException e) {
                log.error("关闭CloseableHttpResponse异常",e);
            }
        }

    }

    /**
     * 获取请求配置
     * @return
     */
    private static RequestConfig getRequestConfig(){
        //请求配置信息
        return RequestConfig.custom()
                .setConnectionRequestTimeout(connectionRequestTimeout)
                .setConnectTimeout(connectTimeout)
                .setSocketTimeout(socketTimeout).build();
    }

    /**
     * 将请求参数封装成List<NameValuePair>
     * @param paramsMap
     * @return
     */
    private static List<NameValuePair> getParamsList(Map<String,String> paramsMap){
        List<NameValuePair> paramsList = null;
        if (!CollectionUtils.isEmpty(paramsMap)) {
            paramsList = new ArrayList<NameValuePair>();
            for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
                paramsList.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
        }
        return paramsList;
    }

    /**
     * 获取字符集
     * @param charset
     * @return
     */
    private static String getCharset(String charset){
        if(StringUtils.isEmpty(charset)) return DEFAULT_CHARSET;
        return charset;
    }

    /**
     * http请求，
     * xml报文  application/xml
     * @param url
     * @param charset 编码
     * @return
     */
    /*public static Map<String,String> postXml(String url,String xmlstr ,String charset){
        Map<String,String> returnMap=new HashMap<String,String>();
        HttpClient httpclient = new HttpClient();
        httpclient.getHttpConnectionManager().getParams().setConnectionTimeout(1000*10);//建立链接超时时间10秒
        PostMethod method  = new PostMethod(url);
        method.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 1000*30);//响应等待超时时间30秒
        try{

            RequestEntity requestEntity = new StringRequestEntity(xmlstr, "application/xml",  charset);
            method.setRequestEntity(requestEntity);

            method.getParams().setContentCharset(charset);
            method.addRequestHeader("Content-Type", "application/xml");

            httpclient.executeMethod(method);
            int code = method.getStatusCode();
            String backinfo = new String(method.getResponseBodyAsString());

            log.info("http响应 HttpStatus "+code);
            log.info("http响应返回字符值 "+backinfo);

            if (code == HttpStatus.SC_OK) {
                if (!StringUtils.isBlank(backinfo)) {
                    returnMap.put(success_key, success_value);
                    returnMap.put(returnStr,backinfo );
                }else{
                    returnMap.put(success_key, success_value);
                    returnMap.put(returnStr,returnStr_value );
                }
            }else{
                log.info("http响应失败 HttpStatus "+code);
                returnMap.put(success_key, "fasle");
                returnMap.put(returnStr,backinfo );
                returnMap.put("mes", "http not SC_OK 响应返回错误");
            }
            return returnMap;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("xmlHttpClient Exception ", e);
            returnMap.put(success_key, "fasle");
            returnMap.put("mes", "http响应出现系统错误:"+e.getMessage());
            return returnMap;
        } finally {
            method.releaseConnection();
        }
    }
*/

    /**
     * @author Martin
     * @param request
     * @param charset
     * @return
     */
    private  static <T> T  excute(HttpRequestBase request,String charset,Class<T> responseType){
        //请求对象
        CloseableHttpClient httpClient=getCloseableHttpClient();
        request.setConfig(getRequestConfig());
        //响 应对象
        CloseableHttpResponse httpResponse = null;
        //返回结果
        T response=null;
        try {
            //调用远程接口
            httpResponse=httpClient.execute(request);
            //查看返回的状态码是否调用成功
            if(httpResponse.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
                log.error("接口调用失败，状态码："+httpResponse.getStatusLine().getStatusCode());
                return null;
            }

            if(responseType==String.class){
                response=(T)EntityUtils.toString(httpResponse.getEntity(),getCharset(charset));
            }else if(responseType==byte[].class){
                response=(T)EntityUtils.toByteArray(httpResponse.getEntity());
            }
        } catch (ClientProtocolException e) {
            log.error("客户端连接异常：",e);
            return null;
        } catch (IOException e) {
            log.error("IO异常：",e);
            return null;
        }finally{
            close(httpResponse);
        }
        return response;
    }

    /**
     * @author Martin
     * POST 文件下载
     * @param url
     * @param param
     * @param charset
     * @return
     */
    public static byte[] postForDownload(String url,Map<String,String> param,String charset){
        log.info("url:"+url);
        log.info("param:"+param);
        log.info("charset:"+charset);

        if(StringUtils.isEmpty(url)){
            return null;
        }

        List<NameValuePair> paramslist=getParamsList(param);

        HttpPost httpPost=new HttpPost(url);
        try {
            if(!CollectionUtils.isEmpty(paramslist)){
                httpPost.setEntity(new UrlEncodedFormEntity(paramslist,getCharset(charset)));
            }
        } catch (UnsupportedEncodingException e) {
            log.error("UrlEncodedFormEntity转换字符异常",e);
            return null;
        }

        return excute(httpPost,charset,byte[].class);
    }

    /**
     * @author Martin
     * get 文件下载
     * @param url
     * @param charset
     * @return
     */
    public static byte[] getForDownload(String url,String charset){
        log.info("url:"+url);
        log.info("charset:"+charset);

        if(StringUtils.isEmpty(url)){
            return null;
        }

        return excute(new HttpGet(url),charset,byte[].class);
    }


    public static void main(String[] args) throws IOException {
        Map<String,String> param=new HashMap<>();
        param.put("ticket","gQFX7zoAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2xuV0lwYmpscHduZS1MYksyVmxkAAIE43MwVgMEAAAAAA==");
        param.put("originId","gh_d87d328725e");
        param.put("expire_seconds","-1");
        param.put("url","http://weixin.qq.com/q/lnWIpbjlpwne-LbK2Vld==");

        byte[] bytes=postForDownload(
                "http://120.132.27.130/weixin/wechat/qrcoder/qrCodePicture",
                param,
                "utf-8"
        );

        OutputStream outputStream=new FileOutputStream(new File("C:\\Users\\Administrator\\Desktop\\qrcode.jpg"));
        outputStream.write(bytes);

        outputStream.flush();
        outputStream.close();
    }
}
